PHP MVC Routing
In een normale PHP toepassing moet elke URL overeenkomen met een specifiek php-bestand. Een URL http: //domain/mijnpagina.php moet bijvoorbeeld overeenkomen met het bestand mijnpagina.php dat de html, inhoud en PHP code bevat voor het weergeven van de gevraagde pagina in de browser.
We gaan routing introduceren zodat we niet aan elke URL een php-bestand moeten koppelen. Met routing kunnen we een URL-patroon definiëren voor de request handler (verzoekbehandelaar). Deze request handler zal overeenomen met een klasse in een bepaald bestand, namelijk de controllerklasse en de actiemethode. Bijvoorbeeld http://domein/student/readAll verwijst naar de readAll
methode van de Student
klasse.
Een route configureren
Een route definieert het URL-patroon en de handler-informatie. De volgende afbeelding illustreert het routeringsproces:
Route patroon
Het URL-patroon begint na het domeinnaamgedeelte in de URL. Het URL-patroon {controller}/{action}/{id} ziet er bijvoorbeeld uit als:
localhost:63343/{controller/{action}/{id}
Alles na localhost:63343/ wordt beschouwd als de naam van de controller. Alles na de naam van de controller wordt beschouwd als de actienaam (methode) en wat daarna komt wordt beschouwd als de waarde van id-parameter.
Als de URL na de domeinnaam niets bevat, zullen de standaardcontroller en actiemethode de aanvraag verwerken. Bijvoorbeeld: http://localhost:63343/ wordt afgehandeld door HomeController
en Index
-methode.
In de volgende tabel wordt weergegeven welke controller, actiemethode en id-parameter verschillende URL's zullen aanspreken:
URL | Controller | Action | Id |
---|---|---|---|
http://localhost:63343/home | HomeController | Index | null |
http://localhost:63343/home/index/123 | HomeController | Index | 123 |
http://localhost:63343/home/about | HomeController | About | null |
http://localhost:63343/home/contact | HomeController | Contact | null |
http://localhost:63343/student | StudentController | Index | null |
http://localhost:63343/student/edit/123 | StudentController | Edit | 123 |
Alle requests omleiden naar index.php
Vermits de URL niet meer naar een PHP-pagina verwijst maar naar een controller en een methode moeten we telkens wanneer de webserver een request ontvangt, een stukje PHP code uitvoeren die de overeenkomstige controller klasse instantiëert en de respectievelijke methode uitvoert. Deze code stoppen we in een bestand met de naam index.php. En dat bestaat staat in de root van de website.
Deze index.php in de root van onze website is het punt waar alle request naar toe gestuurd worden. Dus moeten we ervoor zorgen dat alle trafiek omgeleid wordt naar deze index.php pagina.
- In de root van de website maak je een .htaccess bestand die alle aanvragen omleidt naar index.php:
-
RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.+)$ index.php [QSA,L]
- Als je lokaal werkt met de ingebouwde webserver:
- Om alle requests naar de index.php pagina om te leiden maak je bv. een local.php in de webroot met daarin:
if (file_exists(__DIR__ . '/' . $_SERVER['REQUEST_URI'])) { return false; // serve the requested resource as-is. } else { include_once 'index.php'; }
-
Als je met de ingebouwde webserver fonts wilt laden moet je local.php aanpassen. Er is namelijk een probleem om de iconfonts te laden. In icon-font.css staat in de url een ? na de naam van de te laden font. In het icon-font.css bestand staat bijvoorbeeld:
src: url('fonts/fric-frac.eot?j3d7so');
Deze url met een bestandsnaam gevolgd door een vraagteken en een parameter bestaat natuurlijk niet en zal dus niet geladen worden want de bestandsnaam is:
fonts/fric-frac.eot
Hier volgt code die het vraagteken en alles wat erna komt verwijdert:
<?php function cleanUpFileName ($requestUri) { $queryStart = strpos($requestUri, '?'); if ($queryStart > 0) { $requestUri = substr($requestUri, 0, $queryStart); } return $requestUri; } $uri = $_SERVER['REQUEST_URI']; $uri = cleanUpFileName($uri); if (file_exists(__DIR__ . '/' . $uri)) { return false; // serve the requested resource as-is. } else { include_once 'index.php'; }
- Visual Code: open de terminal (ook in de terminal van PHPStorm) en typ: php -S localhost:63344 local.php:
- meer info hierover:
- Lorna Jane, PHP 5.4 Built In Webserver, 30/12/2010
- PHP built in server and .htaccess mod rewrite
- PHPStorm: je kan de router script opgeven in PHPStorm. Open Edit Configuration:
- Om alle requests naar de index.php pagina om te leiden maak je bv. een local.php in de webroot met daarin:
- We kunnen het gevolg van de instellingen bekijken door gebruik te maken van de PHP
$_SERVER
globale variabele. In de mvc-routing-index.php pagina voeg je de volgende PHP code toe:<!DOCTYPE html> <html lang="en"> <head> <meta charsetUTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>MVC routing in PHP</title> </head> <body> <pre> <?php var_dump($_SERVER); ?> </pre> </body> </html>
- Start de built-in PHP webserver met php -S localhost:63344 local.php.
- typ je een url in gevolgd door een schuine streep /:
http://localhost:63344/, dan vind je:["REQUEST_URI"]=> string(1) "/"
. - typ je dezelfde url in gevolgd door een schuine streep en een de naam van een controller: http://localhost:63344/student krijg je:
["
REQUEST_URI
"]=> string(8) "/student" - typ je dezelfde url in gevolgd door een schuine streep en een de naam van een controller gevolgd door de naam van de actiemethode en een id:
ttp://localhost:63344/student/read/100 krijg je :["
REQUEST_URI
"]=> string(17) "/student/read/100"
- typ je een url in gevolgd door een schuine streep /:
Routing
Met deze kennis gaan we nu een router methode maken. De methode moet op basis van de gegevens in de uri de naam van de controllerklasse, actiemethode en parameter in een associatieve array retourneren.
- als ["
REQUEST_URI
"] leeg is, is de controllernaam standaardHome
en de methodenaamindex
- het eerste deel van de
REQUEST_URI
is de controllernaam; - het tweede deel is de methodenaam, is er geen tweede deel dan is de methodenaam
index
; - het derde deel is de parameter, is er geen derde deel dan is er ook geen parameter
- de functie retourneert een associatieve array met de naam van de controller en de methode en de waarde van de optionele parameter.
- We maken een functie met de naam
getRouting
die de route analyseert. We initialiseren de controllernaam standaard opHome
en de actiemethode opindex
. - De klasse met de naam de
FrontController
, die opgeroepen wordt net vooraleer deController
klasse gebruikt zal worden, plaatsen we in het bestand met de naam fric-frac-simple-crud\vendor\threepennymvc. We zetten die in een map die naast de public map staat omdat de gebruiker daar geen toegang toe geeft. - De
getRouteData
methode in een statische methode. Het is niet bedoeling van instanties van die klasse te maken vermits we er nooit meer dan 1 van nodig zullen hebben. We gebruiken hier alleen OO om onze code te ordenen. - De naam van de namespace is
ThreepennyMVC
. Dat is de 'commerciële' naam die we eraan geven. - Hier is de code:
<?php /** * Created by ModernWays * User: Jef Inghelbrecht * Date: 23/02/2020 * Time: 10:32 */ namespace ThreepennyMVC; class FrontController { public static function getRouteData($uri, $namespaceName = '\\', $controllerName = 'Home', $actionMethodName = 'index') { $controllerClassName = "{$namespaceName}\\Controllers\\{$controllerName}Controller"; $parameterValue = -1; $delimiter = '/'; // remove last \ if present $namespaceName = rtrim($namespaceName, '\\'); // remove first /, if present $uri = ltrim($uri, $delimiter); // make sure there is always a last / $uri = $uri . $delimiter; // remove last /, if present // $uri = rtrim($uri, $delimiter); $pos1 = strpos($uri, $delimiter); if ($pos1 > 0) { // klassenamen in PHP beginnen met een hoofdletter, pascalnotatie $controllerClassName = $namespaceName . '\\Controllers\\' . ucfirst(substr($uri, 0, $pos1)) . 'Controller'; $pos2 = strpos($uri, $delimiter, $pos1 + 1); // echo 'pos2: ' . $pos2 . 'pos 1' . $pos1; if ($pos2 > 0) { // functies in camelcase notatie // echo $uri; $actionMethodName = lcfirst(substr($uri, $pos1 + 1, $pos2 - $pos1 - 1)); // echo self::$actionMethodName; if ($pos2 < strlen($uri)) { //echo 'pos3: ' . $pos3; $pos3 = strpos($uri, $delimiter, $pos2); if ($pos3 > 0) { $parameterValue = rtrim(substr($uri, $pos3 + 1), '/'); } if (empty($parameterValue)) { $parameterValue = -1; } } } } return array( 'controllerClassName' => $controllerClassName, 'actionMethodName' => $actionMethodName, 'parameterValue' => $parameterValue); } }
- We roepen de
getRouteData
functie op in public/index.php en geven we de route als parameter mee, evenals de naam van de namespace waarin de controller staat, de naam van de standaardcontrollernaam en de naam van de standaardactiemethode:?php // var_dump($_SERVER); use ThreepennyMVC\FrontController; include_once('../vendor/threepennymvc/FrontController.php'); // default namespace is root \, Controllername is Home and action is index // otherwise specify it as argument $route = FrontController::getRouteData($_SERVER['REQUEST_URI'], 'Fricfrac', 'Admin', 'index'); ?> <!DOCTYPE html> <html lang="nl"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Fric-frac events</title> </head> <body> <h1>Route</h1> <pre> <?php var_dump($route); ?> </pre> <h1>Server</h1> <pre> <?php var_dump($_SERVER); ?> </pre> </body> </html>
- Open de terminal en ga naar de public map.
- Start de webbrower met:
php -S localhost:63346 local.php - En dat is het resultaat:
Let erop dat de methode de eerste letter van Index naar een kleine letter omzet. Dat is omdat het de afspraak is om methoden in PHP in camelcase te schrijven.
Vergeet niet het .htaccess bestand te hernoemen of te verwijderen als je oefeningen van in de vorige lessen wilt uitproberen!
Bronnen
- Tania Rascia, The Simplest PHP Router, June 21st, 2018
- Routing in ASP.NET MVC